/*
 * i2c.cpp
 *
 *  Created on: Jan 13, 2014
 *      Author: ppks
 */
#include "Arduino.h"
#include "defines.h"
#include "i2c.h"

/******************************************************************************/
/* Global Variable Declaration                                                */
/******************************************************************************/
uint16_t i2c_errors_count = 0;
static uint32_t neutralizeTime;

/******************************************************************************/
/* Local Functions Declaration                                                */
/******************************************************************************/
void CommI2C_WaitForCompletion(void);

/******************************************************************************/
/* Function Code                                                              */
/******************************************************************************/

/*******************************************************************************
* Function Name: CommI2C_Init()
********************************************************************************
* Summary:
* Initializes the I2C block and configure pullups
*
* Parameters:
*  None
*
* Return:
*  None
*
*******************************************************************************/
void CommI2C_Init(void)
{
#if defined(INTERNAL_I2C_PULLUPS)
    I2C_PULLUPS_ENABLE
#else
    I2C_PULLUPS_DISABLE
#endif
    TWSR = 0;                                       /* no prescaler => prescaler = 1 */
    TWBR = ((F_CPU / I2C_CLK_SPEED) - 16) >> 1;     /* set the I2C clock rate to 400kHz */
    TWCR = 1<<TWEN;                                 /* enable TWI module, no interrupt */
}

/*******************************************************************************
* Function Name: CommI2C_SendRepeatStart()
********************************************************************************
* Summary:
*  Tries to acknowledge specified device on the bus
*
* Parameters:
*  uint8_t address - I2C address of the device to be ACK'd
*
* Return:
*  None
*
*******************************************************************************/
void CommI2C_SendRepeatStart(uint8_t addr)
{
    TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN) ; /* send REPEAT START condition */
    CommI2C_WaitForCompletion();                 /* wait until transmission completed */
    TWDR = addr;                                 /* send device address */
    TWCR = (1<<TWINT) | (1<<TWEN);
    CommI2C_WaitForCompletion();                 /* wail until transmission completed */
}

/*******************************************************************************
* Function Name: CommI2C_Stop()
********************************************************************************
* Summary:
*  Stops the I2C block
*
* Parameters:
*  None
*
* Return:
*  None
*
*******************************************************************************/
void CommI2C_SendStop(void)
{
    TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
    //  while(TWCR & (1<<TWSTO));                // <- can produce a blocking state with some WMP clones
}

/*******************************************************************************
* Function Name: CommI2C_Stop()
********************************************************************************
* Summary:
*  Sends a byte to previously addressed device.
*
* Parameters:
*  uint8_t data - input data to be send
*
* Return:
*  None
*
*******************************************************************************/
void CommI2C_Write(uint8_t data)
{
    TWDR = data;
    TWCR = (1<<TWINT) | (1<<TWEN);
    CommI2C_WaitForCompletion();
}

/*******************************************************************************
* Function Name: CommI2C_Read()
********************************************************************************
* Summary:
*  Reads one byte of the data from the bus
*
* Parameters:
*  uint8_t ack -
*
* Return:
*  uint8_t inputByte - 1 byte of the data read from the bus
*
*******************************************************************************/
uint8_t CommI2C_Read(uint8_t ack)
{
    uint8_t inputByte;

    TWCR = (1<<TWINT) | (1<<TWEN) | (ack? (1<<TWEA) : 0);
    CommI2C_WaitForCompletion();

    /* Read the data from the data register */
    inputByte = TWDR;

    if (!ack)
    {
        CommI2C_SendStop();
    }
    return inputByte;
}

/*******************************************************************************
* Function Name: CommI2C_WaitForCompletion()
********************************************************************************
* Summary:
*
*
* Parameters:
*  None
*
* Return:
*  None
*
*******************************************************************************/
void CommI2C_WaitForCompletion(void)
{
    uint16_t count = 255;

    while (!(TWCR & (1<<TWINT)))
    {
        count--;
        if (count==0)
        {                                   /* we are in a blocking state => we don't insist */
            TWCR = 0;                       /* and we force a reset on TWINT register */
            neutralizeTime = micros();      /* we take a timestamp here to neutralize the value during a short delay */
            i2c_errors_count++;
            break;
        }
    }
}

/*******************************************************************************
* Function Name: CommI2C_ReadAck()
********************************************************************************
* Summary:
*
*
* Parameters:
*  None
*
* Return:
*  None
*
*******************************************************************************/
uint8_t CommI2C_ReadAck(void)
{
    return CommI2C_Read(1);
}

/*******************************************************************************
* Function Name: CommI2C_ReadNak()
********************************************************************************
* Summary:
*
*
* Parameters:
*  None
*
* Return:
*  None
*
*******************************************************************************/
uint8_t CommI2C_ReadNak(void)
{
    return CommI2C_Read(0);
}

/*******************************************************************************
* Function Name: CommI2C_ReadRegToBuffer()
********************************************************************************
* Summary:
*
*
* Parameters:
*  None
*
* Return:
*  None
*
*******************************************************************************/
void CommI2C_ReadRegToBuffer(uint8_t addr, uint8_t reg, uint8_t *buf, uint8_t size)
{
    uint8_t *b = buf;

    CommI2C_SendRepeatStart(addr<<1);        /* I2C write direction */
    CommI2C_Write(reg);                      /* Register selection */
    CommI2C_SendRepeatStart((addr<<1) | 1);  /* I2C read direction */

    while (size--)
    {
        /* acknowledge all but the final byte */
        *b++ = CommI2C_Read(size > 0);
    }
}

/*******************************************************************************
* Function Name: CommI2C_WriteReg()
********************************************************************************
* Summary:
*  Writes one byte to the previously acknowledged I2C address
*
* Parameters:
*  None
*
* Return:
*  None
*
*******************************************************************************/
void CommI2C_WriteReg(uint8_t addr, uint8_t reg, uint8_t val)
{
    CommI2C_SendRepeatStart(addr<<1); /* I2C write direction */
    CommI2C_Write(reg);               /* register selection */
    CommI2C_Write(val);               /* value to write in register */
    CommI2C_SendStop();               /* Send STOP condition */
}

/*******************************************************************************
* Function Name: CommI2C_ReadReg()
********************************************************************************
* Summary:
*  Reads one byte from the specified register
*
* Parameters:
*  None
*
* Return:
*  uint8_t val - received byte from the selceted reg address
*
*******************************************************************************/
uint8_t CommI2C_ReadReg(uint8_t addr, uint8_t reg)
{
    uint8_t val;

    /* Read the regsiter from the bus */
    CommI2C_ReadRegToBuffer(addr, reg, &val, 1);

    return val;
}
